#pragma region Licensing
/*
       LOGL::GLib -> LorgonJortle's OpenGL Game Library
       Copyright (C) 2011  Jesse 'LorgonJortle' Wilkerson

       This program is free software: you can redistribute it and/or modify
       it under the terms of the GNU General Public License as published by
       the Free Software Foundation, either version 3 of the License, or
       (at your option) any later version.

       This program is distributed in the hope that it will be useful,
       but WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       GNU General Public License for more details.

       You should have received a copy of the GNU General Public License
       along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#pragma endregion

#include "Matrix.h"

namespace LOGL_GLib
{
     namespace Common
     {

          /******************************************************************************
           Matrix::Matrix();
                    Arguments:
                         void

                    Description:
                         Constructs a matrix object.

                    Returns:
                         The Matrix object.
          *******************************************************************************/
          Matrix::Matrix()
          {
               /* Initialize all matrices to identity. */
               Identity();
          }

          /******************************************************************************
           Matrix::~Matrix();
                    Arguments:
                         void

                    Description:
                         Destructs the matrix, releasing any indebted resources.

                    Returns:
                         void
          *******************************************************************************/
          Matrix::~Matrix()
          {

          }

          /******************************************************************************
           GLfloat* Matrix::operator [](GLint _Index);
                    Arguments:
                         GLint _Index:
                              The number with which to index the matrix.

                    Description:
                         Accesses the specified column in the matrix.

                    Returns:
                        The start of the indexed column.
          *******************************************************************************/
          GLfloat* Matrix::operator [](GLint _Index)
          {
               return &data[height * _Index];
          }

          /******************************************************************************
           GLint Matrix::Width() const;
                    Arguments:
                         void

                    Description:
                         Gets the width of the matrix.

                    Returns:
                        The width of the matrix.
          *******************************************************************************/
          GLint Matrix::Width() const
          {
               return width;
          }

          /******************************************************************************
           GLint Matrix::Height() const;
                    Arguments:
                         void

                    Description:
                         Gets the height of the matrix.

                    Returns:
                        The height of the matrix.
          *******************************************************************************/
          GLint Matrix::Height() const
          {
               return height;
          }

          /******************************************************************************
           GLboolean Matrix::AddRow(GLfloat _Value = 0.0f);
                    Arguments:
                         GLfloat _Value:
                              The value to put into all of the new elements.

                    Description:
                         Adds a row (of the current width) to the matrix.

                    Returns:
                        Whether or not the row was successfully added.
          *******************************************************************************/
          GLboolean Matrix::AddRow(GLfloat _Value)
          {
               /* We can only add a row if we already have a dimension. */
               if(width <= 0 || height <= 0)
                    return GL_FALSE;

               /* Add the new row. */
               for(GLint i = height, j = 0; i < (width * height) + j; i += height, ++j)
                         data.insert(data.begin() + i + j, _Value);

               /* Increase our height. */
               ++height;

               /* It has been added successfully. */
               return GL_TRUE;
          }

          /******************************************************************************
           GLboolean Matrix::AddRow(GLfloat* _Values, GLint _Size);
                    Arguments:
                         GLfloat* _Values:
                              An array of floats to put into the matrix.
                         GLint _Size:
                              The size of the specified array.

                    Description:
                         Adds a row of the specified values (of the current width) to the matrix.

                    Returns:
                        Whether or not the row was successfully added.
          *******************************************************************************/
          GLboolean Matrix::AddRow(GLfloat* _Values, GLint _Size)
          {
               /* We can only add a row if we already have a dimension. */
               if(width <= 0 || height <= 0)
                    return GL_FALSE;

               /* Make sure that enough data was supplied. */
               if(_Size < width)
                    return GL_FALSE;

               /* Add the new column. */
               for(GLint i = height, j = 0; i < (width * height) + j; i += height, ++j)
                    data.insert(data.begin() + i + j, _Values[j]);

               /* Increase our height. */
               ++height;

               /* It has been added successfully. */
               return GL_TRUE;
          }

          /******************************************************************************
           GLboolean Matrix::AddColumn(GLfloat _Value = 0.0f);
                    Arguments:
                         GLfloat _Value:
                              The value to put into all of the new elements.

                    Description:
                         Adds a column (of the current height) to the matrix.

                    Returns:
                        Whether or not the column was successfully added.
          *******************************************************************************/
          GLboolean Matrix::AddColumn(GLfloat _Value)
          {
               /* We can only add a column if we already have a dimension. */
               if(width <= 0 || height <= 0)
                    return GL_FALSE;

               /* Add the new column. */
               for(GLint i = 0; i < height; ++i)
                    data.push_back(_Value);

               /* Increase our width. */
               ++width;

               /* It has been added successfully. */
               return GL_TRUE;
          }

          /******************************************************************************
           GLboolean Matrix::AddColumn(GLfloat* _Values, GLint _Size);
                    Arguments:
                         GLfloat* _Values:
                              An array of floats to put into the matrix.
                         GLint _Size:
                              The size of the specified array.

                    Description:
                         Adds a column of the specified values (of the current height) to the matrix.

                    Returns:
                        Whether or not the column was successfully added.
          *******************************************************************************/
          GLboolean Matrix::AddColumn(GLfloat* _Values, GLint _Size)
          {
               /* We can only add a column if we already have a dimension. */
               if(width <= 0 || height <= 0)
                    return GL_FALSE;

               /* Make sure that enough data was supplied. */
               if(_Size < height)
                    return GL_FALSE;

               /* Copy over as much data as the height supports. */
               for(int i = 0; i < height; ++i)
                    data.push_back(_Values[i]);

               /* Increase our width. */
               ++width;

               /* It has been added successfully. */
               return GL_TRUE;
          }

          /******************************************************************************
           GLvoid Matrix::RemoveRow();
                    Arguments:
                         void

                    Description:
                         Removes a row from the matrix.

                    Returns:
                        void
          *******************************************************************************/
          GLvoid Matrix::RemoveRow()
          {
               /* Check if there's anything to remove. */
               if(width <= 0 || height <= 0)
                    return;

               /* Remove the row. */
               for(GLint i = height - 1, j = 0; j < width; i += height - 1, ++j)
                         data.erase(data.begin() + i);

               /* Adjust the dimensions accordingly. */
               if(--height == 0)
                    width = 0;
          }

          /******************************************************************************
           GLvoid Matrix::RemoveColumn();
                    Arguments:
                         void

                    Description:
                         Removes a column from the matrix.

                    Returns:
                        void
          *******************************************************************************/
          GLvoid Matrix::RemoveColumn()
          {
               /* Check if there's anything to remove. */
               if(width <= 0 || height <= 0)
                    return;

               /* Remove the row. */
               data.erase(data.end() - height, data.end());

               /* Adjust the dimensions accordingly. */
               if(--width == 0)
                    height = 0;
          }

          /******************************************************************************
           GLvoid Matrix::Assign(GLfloat* _Data, GLuint _Size);
                    Arguments:
                         GLfloat* _Data:
                              An array of floats to assing to the matrix.
                         GLuint _Size:
                              The size of the specified array.

                    Description:
                         Assigns the specified data to the matrix.

                    Returns:
                        void
          *******************************************************************************/
          GLvoid Matrix::Assign(GLfloat* _Data, GLuint _Size)
          {
               /* Make sure the right amount of data has been specified. */
               if(_Size <= 0 || _Size % 2 != 0)
                    return;

               /* Resize the matrix. */
               data.resize(_Size);

               /* Copy over the specified values. */
               data.assign(_Data, _Data + _Size);
          }

          /******************************************************************************
           GLvoid Matrix::Assign(std::vector<GLfloat>& _Data);
                    Arguments:
                         std::vector<GLfloat>& _Data:
                              An array of data to assign to the matrix.

                    Description:
                         Assigns the specified data to the matrix.

                    Returns:
                        void
          *******************************************************************************/
          GLvoid Matrix::Assign(std::vector<GLfloat>& _Data)
          {
               /* Make sure the right amount of data has been specified. */
               if(_Data.size() <= 0 || _Data.size() % 2 != 0)
                    return;

               /* Resize the matrix. */
               data.resize(_Data.size());

               /* Copy over the specified values. */
               data.assign(_Data.begin(), _Data.end());
          }

          /******************************************************************************
           GLvoid Matrix::Identity();
                    Arguments:
                         void

                    Description:
                         Assigns this to the identity matrix.

                    Returns:
                        void
          *******************************************************************************/
          GLvoid Matrix::Identity()
          {
               /* Clear out the existing data. */
               data.clear();
               data.reserve(16);

               data.push_back(1.0f);
               data.push_back(0.0f);
               data.push_back(0.0f);
               data.push_back(0.0f);

               data.push_back(0.0f);
               data.push_back(1.0f);
               data.push_back(0.0f);
               data.push_back(0.0f);

               data.push_back(0.0f);
               data.push_back(0.0f);
               data.push_back(1.0f);
               data.push_back(0.0f);

               data.push_back(0.0f);
               data.push_back(0.0f);
               data.push_back(0.0f);
               data.push_back(1.0f);

               width = 4;
               height = 4;
          }

          /******************************************************************************
           static Matrix Matrix::CreateIdentity();
                    Arguments:
                         void

                    Description:
                         Calculates an identity matrix.

                    Returns:
                        An identity matrix.
          *******************************************************************************/
          Matrix Matrix::CreateIdentity()
          {
               /* All matrices are identity by default. */
               return Matrix();
          }

          /******************************************************************************
           GLvoid Matrix::Zero(GLint _Width = 4, GLint _Height = 4);
                    Arguments:
                         GLint _Width:
                              The width of the matrix to create.
                         GLint _Height:
                              The height of the matrix to create.

                    Description:
                         Assigns this to the zero matrix.

                    Returns:
                        void
          *******************************************************************************/
          GLvoid Matrix::Zero(GLint _Width, GLint _Height)
          {
               /* Clear any existing data. */
               data.clear();
               data.reserve(_Width * _Height);

               /* Assign all zeros. */
               for(GLint i = 0; i < _Width * _Height; ++i)
                    data.push_back(0);

               /* Reset the dimensions. */
               width = _Width;
               height = _Height;
          }

          /******************************************************************************
           static Matrix Matrix::Zero(GLint _Width = 4, GLint _Height = 4);
                    Arguments:
                         GLint _Width:
                              The width of the matrix to create.
                         GLint _Height:
                              The height of the matrix to create.

                    Description:
                         Calculates a zero matrix.

                    Returns:
                        A zero matrix of the specified size.
          *******************************************************************************/
          Matrix Matrix::CreateZero(GLint _Width, GLint _Height)
          {
               /* Create an identity matrix. */
               Matrix mat;
               mat.data.clear();
               mat.data.resize(_Width * _Height);

               /* Assign all zeros. */
               for(GLint i = 0; i < _Width * _Height; ++i)
                    mat.data.push_back(0.0f);

               /* Assign the dimensions. */
               mat.width = _Width;
               mat.height = _Height;

               /* Return the matrix. */
               return mat;
          }

          /******************************************************************************
           static Matrix Matrix::CreatePerspectiveProjection(GLfloat _Left,
                              GLfloat _Right, GLfloat _Bottom, GLfloat _Top,
                              GLfloat _Near, GLfloat _Far);
                    Arguments:
                         GLfloat _Left:
                              The X minimum of the frustum.
                         GLfloat _Right:
                              The X maximum of the frustum.
                         GLfloat _Bottom:
                              The Y minimum of the frustum.
                         GLfloat _Top:
                              The Y maximum of the frustum.
                         GLfloat _Near:
                              The Z minimum of the frustum.
                         GLfloat _Far:
                              The Z maximum of the frustum.

                    Description:
                         Calculates a perspective view frustum with the specified dimensions.

                    Returns:
                        A perspective view frustum with the specified dimensions.
          *******************************************************************************/
          Matrix Matrix::CreatePerspectiveProjection(GLfloat _Left, GLfloat _Right, GLfloat _Bottom, GLfloat _Top, GLfloat _Near, GLfloat _Far)
          {
               /* Create the matrix we'll be returning. */
               Matrix mat;

               /* Fill the matrix with the specified members to create the frustum. */
               /*
               GLfloat arr[16] = {
                         (2 * _Near) / (_Right - _Left), 0.0f, (_Right + _Left) / (_Right - _Left), 0.0f,
                         0.0f,  (2 * _Near) / (_Top - _Bottom),   (_Top + _Bottom) / (_Top - _Bottom), 0.0f,
                         0.0f,  0.0f,   -(_Far  + _Near) / (_Far  - _Near), (-2 * _Far * _Near) / (_Far - _Near),
                         0.0f, 0.0f, -1.0f, 0.0f
               }; */

               GLfloat arr[16] = {
                         (2 * _Near) / (_Right - _Left), 0.0f, 0.0f, 0.0f,
                         0.0f, (2 * _Near) / (_Top - _Bottom), 0.0f, 0.0f,
                         (_Right + _Left) / (_Right  - _Left), (_Top + _Bottom) / (_Top - _Bottom), -(_Far + _Near) / (_Far  - _Near), -1.0f,
                         0.0f, 0.0f, (-2 * _Far * _Near) / (_Far - _Near), 0.0f
               };
               mat.Assign(arr, 16);

               /* Return the new matrix. */
               return mat;
          }

          /******************************************************************************
           static Matrix Matrix::CreateOrthoProjection(GLfloat _Left, GLfloat _Right,
                              GLfloat _Bottom, GLfloat _Top, GLfloat _Near, GLfloat _Far);
                    Arguments:
                         GLfloat _Left:
                              The X minimum of the frustum.
                         GLfloat _Right:
                              The X maximum of the frustum.
                         GLfloat _Bottom:
                              The Y minimum of the frustum.
                         GLfloat _Top:
                              The Y maximum of the frustum.
                         GLfloat _Near:
                              The Z minimum of the frustum.
                         GLfloat _Far:
                              The Z maximum of the frustum.

                    Description:
                         Calculates an orthographic view frustum with the specified dimensions.

                    Returns:
                        An orthographic view frustum with the specified dimensions.
          *******************************************************************************/
          Matrix Matrix::CreateOrthoProjection(GLfloat _Left, GLfloat _Right, GLfloat _Bottom, GLfloat _Top, GLfloat _Near, GLfloat _Far)
          {
               /* Create the matrix we'll be returning. */
               Matrix mat;

               /* Fill the matrix with the specified members to create the frustum. */
               /*
               GLfloat arr[16] = {
                    2.0f / (_Right - _Left), 0.0f, 0.0f, -(_Right + _Left) / (_Right - _Left),
                    0.0f, 2.0f / (_Top - _Bottom), 0.0f, -(_Top + _Bottom) / (_Top - _Bottom),
                    0.0f, 0.0f, -2.0f / (_Far - _Near), -(_Far + near) / (_Far - _Near),
                   0.0f, 0.0f, 0.0f, 1.0f
               }; */
               GLfloat arr[16] = { 2.0f / (_Right - _Left), 0.0f, 0.0f, 0.0f,
                                               0.0f, 2.0f / (_Top - _Bottom),  0.0f, 0.0f,
                                               0.0f, 0.0f, -2.0f / (_Far - _Near), 0.0f,
                                               -(_Right + _Left) / (_Right - _Left), -(_Top + _Bottom) / (_Top - _Bottom), -(_Far + _Near) / (_Far - _Near), 1.0f };
               mat.Assign(arr, 16);

               /* Return the new matrix. */
               return mat;
          }

          /******************************************************************************
           static Matrix Matrix::CreateTranslation(const Vector3& _Translation);
                    Arguments:
                         const Vector3& _Translation:
                              The vector to (relatively) translate to.

                    Description:
                         Calculates a translation matrix from the specified point.

                    Returns:
                        A translation matrix from the specified point.
          *******************************************************************************/
          Matrix Matrix::CreateTranslation(const Vector3& _Translation)
          {
               Matrix mat;
               GLfloat arr[16] = {     1.0f, 0.0f, 0.0f, 0.0f,
                                                       0.0f, 1.0f, 0.0f, 0.0f,
                                                       0.0f, 0.0f, 1.0f, 0.0f,
                                                       _Translation.X, _Translation.Y, _Translation.Z, 1.0f };
               mat.Assign(arr, 16);

               return mat;
          }

          /******************************************************************************
           static Matrix Matrix::CreateScale(const Vector3& _Scale);
                    Arguments:
                         const Vector3& _Scale:
                              The vector of relative ratios to scale by.

                    Description:
                         Calculates a scale matrix from the specified scale ratios.

                    Returns:
                        A scale matrix from the specified scale ratios.
          *******************************************************************************/
          Matrix Matrix::CreateScale(const Vector3& _Scale)
          {
               Matrix mat;
               GLfloat arr[16] = {  _Scale.X,      0.0f,            0.0f,             0.0f,
                                                    0.0f,                _Scale.Y,  0.0f,             0.0f,
                                                    0.0f,                0.0f,           _Scale.Z,    0.0f,
                                                    0.0f,                0.0f,           0.0f,             1.0f };
               mat.Assign(arr, 16);

               return mat;
          }

          /******************************************************************************
           static Matrix Matrix::CreateRotationX(GLfloat _Radians);
                    Arguments:
                         GLfloat _Radians:
                              The amount (in radians) to rotate around the X axis.

                    Description:
                         Calculates a rotation matrix around the X axis of the following radians.

                    Returns:
                        A rotation matrix around the X axis of the following radians.
          *******************************************************************************/
          Matrix Matrix::CreateRotationX(GLfloat _Radians)
          {
               GLfloat deg = Math::ToDegrees(_Radians);

               Matrix mat;
               GLfloat arr[16] = {    1.0f, 0.0f,          0.0f,           0.0f,
                                                   0.0f, cos(deg), -sin(deg), 0.0f,
                                                   0.0f, sin(deg),  cos(deg), 0.0f,
                                                   0.0f, 0.0f,           0.0f,          1.0f };
               mat.Assign(arr, 16);

               return mat;
          }

          /******************************************************************************
           static Matrix Matrix::CreateRotationY(GLfloat _Radians);
                    Arguments:
                         GLfloat _Radians:
                              The amount (in radians) to rotate around the Y axis.

                    Description:
                         Calculates a rotation matrix around the Y axis of the following radians.

                    Returns:
                        A rotation matrix around the Y axis of the following radians.
          *******************************************************************************/
          Matrix Matrix::CreateRotationY(GLfloat _Radians)
          {
               GLfloat deg = Math::ToDegrees(_Radians);

               Matrix mat;
               GLfloat arr[16] = { cos(deg),  0.0f,    sin(deg),  0.0f,
                                                   0.0f,            1.0f,    0.0f,          0.0f,
                                                   -sin(deg), 0.0f,    cos(deg),  0.0f,
                                                   0.0f,           0.0f,    0.0f,           1.0f };

                                                   /*
               GLfloat arr[16] = { cos(deg), 0.0f, -sin(deg), 0.0f,
                                                0.0f, 1.0f, 0.0f, 0.0f,
                                                sin(deg), 0.0f, cos(deg), 0.0f,
                                                0.0f, 0.0f, 0.0f, 1.0f };
                                                */
               mat.Assign(arr, 16);

               return mat;
          }

          /******************************************************************************
           static Matrix Matrix::CreateRotationZ(GLfloat _Radians);
                    Arguments:
                         GLfloat _Radians:
                              The amount (in radians) to rotate around the Z axis.

                    Description:
                         Calculates a rotation matrix around the Z axis of the following radians.

                    Returns:
                        A rotation matrix around the Z axis of the following radians.
          *******************************************************************************/
          Matrix Matrix::CreateRotationZ(GLfloat _Radians)
          {
               GLfloat deg = Math::ToDegrees(_Radians);

               Matrix mat;
               GLfloat arr[16] = { cos(deg), -sin(deg),   0.0f, 0.0f,
                                                   sin(deg), cos(deg),     0.0f, 0.0f,
                                                   0.0f,          0.0f,              1.0f, 0.0f,
                                                   0.0f,          0.0f,              0.0f, 1.0f };
               mat.Assign(arr, 16);

               return mat;
          }

          /******************************************************************************
           GLvoid Matrix::Translate(const Vector3& _Translation);
                    Arguments:
                         const Vector3& _Translation:
                              The vector to (relatively) translate to.

                    Description:
                         Translates this matrix by the specified vector.

                    Returns:
                        void
          *******************************************************************************/
          GLvoid Matrix::Translate(const Vector3& _Translation)
          {
               Matrix mat = Matrix::CreateTranslation(_Translation);
               (*this) *= mat;
          }

          /******************************************************************************
           GLvoid Matrix::Scale(const Vector3& _Scale);
                    Arguments:
                         const Vector3& _Scale:
                              The vector of relative ratios to scale by.

                    Description:
                         Scales this matrix by the specified vector.

                    Returns:
                        void
          *******************************************************************************/
          GLvoid Matrix::Scale(const Vector3& _Scale)
          {
               Matrix mat = Matrix::CreateScale(_Scale);
               (*this) *= mat;
          }

          /******************************************************************************
           GLvoid Matrix::RotateX(GLfloat _Radians);
                    Arguments:
                         GLfloat _Radians:
                              The amount (in radians) to rotate around the X axis.

                    Description:
                         Rotates this matrix around the X axis by the specified vector.

                    Returns:
                        void
          *******************************************************************************/
          GLvoid Matrix::RotateX(GLfloat _Radians)
          {
               Matrix mat = Matrix::CreateRotationX(_Radians);
               (*this) *= mat;;
          }

          /******************************************************************************
           GLvoid Matrix::RotateY(GLfloat _Radians);
                    Arguments:
                         GLfloat _Radians:
                              The amount (in radians) to rotate around the Y axis.

                    Description:
                         Rotates this matrix around the Y axis by the specified vector.

                    Returns:
                        void
          *******************************************************************************/
          GLvoid Matrix::RotateY(GLfloat _Radians)
          {
               Matrix mat = Matrix::CreateRotationY(_Radians);
               (*this) *= mat;
          }

          /******************************************************************************
           GLvoid Matrix::RotateZ(GLfloat _Radians);
                    Arguments:
                         GLfloat _Radians:
                              The amount (in radians) to rotate around the Z axis.

                    Description:
                         Rotates this matrix around the Z axis by the specified vector.

                    Returns:
                        void
          *******************************************************************************/
          GLvoid Matrix::RotateZ(GLfloat _Radians)
          {
               Matrix mat = Matrix::CreateRotationZ(_Radians);
               (*this) *= mat;
          }

          /******************************************************************************
           Matrix Matrix::operator -();
                    Arguments:
                         void

                    Description:
                         Calculates the negated version of this.

                    Returns:
                        The negated version of this.
          *******************************************************************************/
          Matrix Matrix::operator -()
          {
               /* Invert all components of the matrix. */
               return (*this) * -1.0f;
          }

          /******************************************************************************
           Matrix Matrix::operator +(Matrix& _Mat);
                    Arguments:
                         Matrix& _Mat:
                              The matrix to add to this.

                    Description:
                         Adds the specified matrix with this.

                    Returns:
                        The sum of the specified matrix and this.
          *******************************************************************************/
          Matrix Matrix::operator +(Matrix& _Mat)
          {
               /* Make sure the  matrices have the same dimensions. */
               if(Width() != _Mat.Width() || Height() != _Mat.Height())
                    return Matrix::CreateZero();

               /* The matrix we'll be returning. */
               Matrix mat = (*this);

               /* Assign the appropriate values. */
               for(GLint y = 0; y < height; ++y)
                    for(GLint x = 0; x < width; ++x)
                         mat[x][y] += _Mat[x][y];

               /* Return the matrix. */
               return mat;
          }

          /******************************************************************************
           GLvoid Matrix::operator +=(Matrix& _Mat);
                    Arguments:
                         Matrix& _Mat:
                              The matrix to add to this.

                    Description:
                         Adds the specified matrix to this.

                    Returns:
                        void
          *******************************************************************************/
          GLvoid Matrix::operator +=(Matrix& _Mat)
          {
               /* Make sure the  matrices have the same dimensions. */
               if(Width() != _Mat.Width() || Height() != _Mat.Height())
                    return;

               /* Assign the appropriate values. */
               for(GLint y = 0; y < height; ++y)
                    for(GLint x = 0; x < width; ++x)
                         (*this)[x][y] += _Mat[x][y];
          }

          /******************************************************************************
           Matrix Matrix::operator -(Matrix& _Mat);
                    Arguments:
                         Matrix& _Mat:
                              The matrix to subtract from this.

                    Description:
                         Subtracts the specified matrix from this.

                    Returns:
                        The difference of the specified matrix and this.
          *******************************************************************************/
          Matrix Matrix::operator -(Matrix& _Mat)
          {
               /* Make sure the  matrices have the same dimensions. */
               if(Width() != _Mat.Width() || Height() != _Mat.Height())
                    return Matrix::CreateZero();

               /* The matrix we'll be returning. */
               Matrix mat = *this;

               /* Assign the appropriate values. */
               for(GLint y = 0; y < height; ++y)
                    for(GLint x = 0; x < width; ++x)
                         mat[x][y] -= _Mat[x][y];

               /* Return the matrix. */
               return mat;
          }

          /******************************************************************************
           GLvoid Matrix::operator +=(Matrix& _Mat);
                    Arguments:
                         Matrix& _Mat:
                              The matrix to subtract from this.

                    Description:
                         Subtracts the specified matrix from this.

                    Returns:
                        void
          *******************************************************************************/
          GLvoid Matrix::operator -=(Matrix& _Mat)
          {
               /* Make sure the  matrices have the same dimensions. */
               if(Width() != _Mat.Width() || Height() != _Mat.Height())
                    return;

               /* Assign the appropriate values. */
               for(GLint y = 0; y < height; ++y)
                    for(GLint x = 0; x < width; ++x)
                         (*this)[x][y] -= _Mat[x][y];
          }

          /******************************************************************************
           Matrix Matrix::operator *(GLfloat _Scalar);
                    Arguments:
                         GLfloat _Scalar:
                              A float to scale the matrix by.

                    Description:
                         Scales the matrix.

                    Returns:
                       The scaled matrix.
          *******************************************************************************/
          Matrix Matrix::operator *(GLfloat _Scalar)
          {
               /* Make sure there is a matrix to work with. */
               if(Width() <= 0 || Height() <= 0)
                    return Matrix::CreateZero();

               /* The matrix we'll be returning. */
               Matrix mat = *this;

               /* Assign the appropriate values. */
               for(GLint y = 0; y < height; ++y)
                    for(GLint x = 0; x < width; ++x)
                         mat[x][y] *= _Scalar;

               /* Return the matrix. */
               return mat;
          }

          /******************************************************************************
           GLvoid Matrix::operator *=(GLfloat _Scalar);
                    Arguments:
                         GLfloat _Scalar:
                              A float to scale this matrix by.

                    Description:
                         Scales this matrix.

                    Returns:
                       void
          *******************************************************************************/
          GLvoid Matrix::operator *=(GLfloat _Scalar)
          {
               /* Make sure there is a matrix to work with. */
               if(Width() <= 0 || Height() <= 0)
                    return;

               /* Assign the appropriate values. */
               for(GLint y = 0; y < height; ++y)
                    for(GLint x = 0; x < width; ++x)
                         (*this)[x][y] *= _Scalar;
          }

          /******************************************************************************
           Matrix Matrix::operator *(Matrix& _Mat);
                    Arguments:
                         Matrix& _Mat:
                              A matrix to multiply against.

                    Description:
                         Calculates the product of two matrices.

                    Returns:
                       The product of this and the specified matrix.
          *******************************************************************************/
          Matrix Matrix::operator *(Matrix& _Mat)
          {
               /* Make sure the two matrices are compatible. */
               if(Width() != _Mat.Height() || Width() == 0 || Height() == 0 || _Mat.Width() <= 0 || _Mat.Height() <= 0)
                    return Matrix::CreateZero();

               /* The matrix we'll be returning. */
               Matrix mat = Matrix::CreateZero(_Mat.Width(), Height());

               /* Loop through all of our rows. */
               for(GLint h = 0; h < Height(); ++h)
                    /* Loop through the other's columns. */
                    for(GLint c = 0; c < _Mat.Width(); ++c)
                         /* Loop through our columns. */
                         for(GLint w = 0; w < Width(); ++w)
                              mat[c][h] += (*this)[w][h] *_Mat[c][w];

               /* Return the matrix. */
               return mat;
          }

          /******************************************************************************
           GLvoid Matrix::operator *=(Matrix& _Mat);
                    Arguments:
                         Matrix& _Mat:
                              A matrix to multiply against.

                    Description:
                         Multiplies the specified matrix onto this.

                    Returns:
                       void
          *******************************************************************************/
          GLvoid Matrix::operator *=(Matrix& _Mat)
          {
               /* Make sure the two matrices are compatible. */
               if(Width() != _Mat.Height() || Width() == 0 || Height() == 0 || _Mat.Width() <= 0 || _Mat.Height() <= 0)
                    return;

               /* Temp matrix. */
               Matrix mat = Matrix::CreateZero(_Mat.Width(), Height());

               /* Loop through all of our rows. */
               for(GLint h = 0; h < Height(); ++h)
                    /* Loop through the other's columns. */
                    for(GLint c = 0; c < _Mat.Width(); ++c)
                         /* Iterator through our columns. */
                         for(GLint w = 0; w < Width(); ++w)
                              mat[c][h] += (*this)[w][h] *_Mat[c][w];

               (*this) = mat;
          }

          /******************************************************************************
           std::string Matrix::ToString();
                    Arguments:
                         void

                    Description:
                         Converts the matrix into string format.

                    Returns:
                       A string-formatted version of this.
          *******************************************************************************/
          std::string Matrix::ToString()
          {
               /* Create our string stream. */
               std::stringstream stream;

               /* Write each element to the stream. */
               for(GLint y = 0; y < height; ++y)
               {
                    for(GLint x = 0; x < width; ++x)
                         stream << (*this)[x][y] << " ";

                    stream << std::endl;
               }

               /* Return the stream. */
               return stream.str();
          }

     } /* Namespace Common. */
} /* Namespace LOGL_GLib. */
